"""
"""
import marshal
import cStringIO
import struct

import simplejson
from twisted.internet import protocol
from twisted.python import log
from zope import interface

from fjork import ifjork


class FjorkClientProtocol(protocol.Protocol):
    """Fjork clients are basically just
    line receivers that handle several basic
    messages:
      - run
      - shutdown
      - status
    
    """
    interface.implements(ifjork.IFjorkClientProtocol)

    FORMAT = simplejson
    LTYPE = "L"
    NBYTES = len(struct.pack(LTYPE, 0))

    def __init__(self):
        log.msg("creating fjork client protocol")
        self.data = cStringIO.StringIO()
        self.messageLength = None

    def connectionMade(self):
        log.msg("connection made")

    def connectionLost(self, reason):
        log.msg("lost connection")

    def dataReceived(self, data):
        log.msg("receiving data: %s bytes"%(len(data),))
        log.msg("data:", data)
        self.data.write(data)
        
        if self.messageLength is None:
            if len(self.data.getvalue()) >= self.NBYTES:
                bytes = self.data.getvalue()[:self.NBYTES]
                length = struct.unpack(self.LTYPE, bytes)[0]
                self.messageLength = length
        log.msg("messageLength:", self.messageLength)
        if self.messageLength is not None:
            if len(self.data.getvalue()) > self.messageLength:
                serializedData = self.data.getvalue()[self.NBYTES:]
                data = self.FORMAT.loads(serializedData)
                message = data["message"].upper()
                if message == "RUN":
                    result = self.run()
                    self.transport.write(result)
                    self.transport.loseConnection()
                elif message == "SHUTDOWN":
                    self.shutdown()
                else:
                    log.msg("Undefined message type: %s"%(message,))

    def run(self):
        """Runs the specified Job"""
        j = self.factory.job()
        return j.fjork()

    def shutdown(self):
        """Shutdowns the process"""

    def status(self):
        """Returns info about this process
          - uptime
          - memory usage
        """


class WebkitClientProtocol(FjorkClientProtocol):
    interface.implements(ifjork.IFjorkClientProtocol)
    FORMAT = marshal

    def dataReceived(self, data):
        log.msg("receiving data: %s bytes"%(len(data),))
        log.msg("data:", data)
        self.data.write(data)
        
        if self.messageLength is None:
            if len(self.data.getvalue()) >= self.NBYTES:
                bytes = self.data.getvalue()[:self.NBYTES]
                self.messageLength = self.FORMAT.loads(bytes)
        log.msg("messageLength:", self.messageLength)
        if self.messageLength is not None:
            if len(self.data.getvalue()) > self.messageLength:
                serializedData = self.data.getvalue()[self.NBYTES:]
                data = self.FORMAT.loads(serializedData)
                message = data["message"].upper()
                if message == "RUN":
                    result = self.run()
                    self.transport.write(result)
                    self.transport.loseConnection()
                elif message == "SHUTDOWN":
                    self.shutdown()
                else:
                    log.msg("Undefined message type: %s"%(message,))


class FjorkClientFactory(protocol.ClientFactory):
    """Fjork Factory

    """
    interface.implements(ifjork.IFjorkClientFactory)
    protocol = FjorkClientProtocol

    def __init__(self, job):
        self.job = job


class WebkitClientFactory(FjorkClientFactory):
    interface.implements(ifjork.IFjorkClientFactory)
    protocol = WebkitClientProtocol

        
class FjorkServerProtocol(FjorkClientProtocol):
    """Fjork clients are basically just
    line receivers that handle several basic
    messages:
      - run
      - shutdown
      - status
    
    """
    interface.implements(ifjork.IFjorkServerProtocol)
    
    def __init__(self):
        log.msg("fjork server protocol instantiated:")
        self.data = cStringIO.StringIO()
        self.messageLength = None

    def connectionMade(self):
        """Sends message to subprocess"""
        log.msg("connection made")
        message = {"message":"RUN",
                   #"request":self.factory.request,
                   "request":"/"}
        data = self.FORMAT.dumps(message)
        size = len(data)
        binary = ""
        binary += struct.pack(self.LTYPE, size)
        binary += struct.pack("%ss"%(size), data)
        self.transport.write(binary)

    def connectionLost(self, reason):
        """Calls back application with result"""
        log.msg("lost connection")
        self.factory.deferred.callback(self.data.getvalue())

    def dataReceived(self, data):
        """Receives results from subprocess"""
        log.msg("receiving from child process - data: %s"%(data,))
        self.data.write(data)


class WebkitServerProtocol(FjorkServerProtocol):
    interface.implements(ifjork.IFjorkServerProtocol)
    FORMAT = marshal

    def connectionMade(self):
        """Sends message to subprocess"""
        log.msg("connection made")
        # here's the real twisted request, how do we pack this into a pile
        # of crap understood by webware?
        # realTwistedRequest = self.factory.request
        message = {"message":"RUN",
                   "request":"/",
                   "format":"CGI",
                   "environ":{},
                   }
        data = self.FORMAT.dumps(message)
        size = len(data)
        message = ""
        message += self.FORMAT.dumps(size)
        message += data
        self.transport.write(message)


class FjorkServerFactory(protocol.ClientFactory):
    """Fjork Factory

    Even though i'm currently calling this a 'server' it
    is really just a client protocol running on
    the server side.

    """
    interface.implements(ifjork.IFjorkServerFactory)
    protocol = FjorkServerProtocol

    def __init__(self, deferred, request):
        self.deferred = deferred
        self.request = request


class WebkitServerFactory(FjorkServerFactory):
    interface.implements(ifjork.IFjorkServerFactory)
    protocol = WebkitServerProtocol



class FjorkProcessProtocol(protocol.ProcessProtocol):
    def __init__(self, address, deferred): 
        self.address = address
        self.deferred = deferred
        self.buffer = cStringIO.StringIO()
        self.childProcessReady = False
        
    def connectionMade(self):
        log.msg("process protocol:", "connectionMade")

    def outReceived(self, data):
        log.msg("process protocol:", "outReceived", data)
        if not self.childProcessReady:
            self.buffer.write(data)
            if "Fjork is ready!" in self.buffer.getvalue():
                self.childProcessReady = True
                self.deferred.callback(self.address)
                
    def errReceived(self, data):
        log.msg("process protocol:", "errReceived", data)

    def inConnectionLost(self):
        log.msg("process protocol:", "inConnectionLost")

    def outConnectionLost(self):
        log.msg("process protocol:", "outConnectionLost")
    
    def errConnectionLost(self):
        log.msg("process protocol:", "errConnectionLost")

    def processExited(self, reason):
        """Called when child process exits:

        reason should be either:
          - twisted.internet.error.ProcessDone
          - twisted.internet.error.ProcessTerminated
        """
        # exitCode = reason.value.exitCode
        # error = 'FjorkProcess.processExited: %s\n' % (exitCode,)
        # log.msg(error)
        # self.deferred.errback(reason)
        pass

    def processEnded(self, reason):
        """Called when the child process exits and all file descriptors
        associated with it have been closed:

        reason should be either:
          - twisted.internet.error.ProcessDone
          - twisted.internet.error.ProcessTerminated
        """
        exitCode = reason.value.exitCode
        error = 'FjorkProcess.processEnded: %s\n' % (exitCode,)
        log.msg(error)
        self.deferred.errback(reason)


